#![allow(unused_extern_crates)]
extern crate serde_ignored;
extern crate tokio_core;
extern crate native_tls;
extern crate hyper_tls;
extern crate openssl;
extern crate mime;
extern crate chrono;
extern crate percent_encoding;
extern crate url;
{{#apiUsesUuid}}
extern crate uuid;
{{/apiUsesUuid}}
{{#apiUsesMultipart}}
extern crate multipart;
{{/apiUsesMultipart}}

use std::sync::Arc;
use std::marker::PhantomData;
use futures::{Future, future, Stream, stream};
use hyper;
use hyper::{Request, Response, Error, StatusCode};
use hyper::header::{Headers, ContentType};
use self::url::form_urlencoded;
use mimetypes;
{{#apiUsesMultipart}}
use self::multipart::server::Multipart;
use self::multipart::server::save::SaveResult;
use std::fs;
{{/apiUsesMultipart}}
use serde_json;
{{#usesXml}}
use serde_xml_rs;
{{/usesXml}}

#[allow(unused_imports)]
use std::collections::{HashMap, BTreeMap};
#[allow(unused_imports)]
use swagger;
use std::io;

#[allow(unused_imports)]
use std::collections::BTreeSet;

pub use swagger::auth::Authorization;
use swagger::{ApiError, XSpanId, XSpanIdString, Has, RequestParser};
use swagger::auth::Scopes;
use swagger::headers::SafeHeaders;

use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
     {{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
     };
#[allow(unused_imports)]
use models;

pub mod context;

header! { (Warning, "Warning") => [String] }

mod paths {
    extern crate regex;

    lazy_static! {
        pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(vec![
{{#pathSet}}
            r"^{{{basePathWithoutHost}}}{{{pathRegEx}}}"{{^-last}},{{/-last}}
{{/pathSet}}
        ]).unwrap();
    }
{{#pathSet}}
    pub static ID_{{{PATH_ID}}}: usize = {{{index}}};
{{#hasPathParams}}
    lazy_static! {
        pub static ref REGEX_{{{PATH_ID}}}: regex::Regex = regex::Regex::new(r"^{{{basePathWithoutHost}}}{{{pathRegEx}}}").unwrap();
    }
{{/hasPathParams}}
{{/pathSet}}
}

pub struct NewService<T, C> {
    api_impl: Arc<T>,
    marker: PhantomData<C>,
}

impl<T, C> NewService<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{
    pub fn new<U: Into<Arc<T>>>(api_impl: U) -> NewService<T, C> {
        NewService{api_impl: api_impl.into(), marker: PhantomData}
    }
}

impl<T, C> hyper::server::NewService for NewService<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{
    type Request = (Request, C);
    type Response = Response;
    type Error = Error;
    type Instance = Service<T, C>;

    fn new_service(&self) -> Result<Self::Instance, io::Error> {
        Ok(Service::new(self.api_impl.clone()))
    }
}

pub struct Service<T, C> {
    api_impl: Arc<T>,
    marker: PhantomData<C>,
}

impl<T, C> Service<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static {
    pub fn new<U: Into<Arc<T>>>(api_impl: U) -> Service<T, C> {
        Service{api_impl: api_impl.into(), marker: PhantomData}
    }
}

impl<T, C> hyper::server::Service for Service<T, C>
where
    T: Api<C> + Clone + 'static,
    C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{
    type Request = (Request, C);
    type Response = Response;
    type Error = Error;
    type Future = Box<dyn Future<Item=Response, Error=Error>>;

    fn call(&self, (req, mut context): Self::Request) -> Self::Future {
        let api_impl = self.api_impl.clone();
        let (method, uri, _, headers, body) = req.deconstruct();
        let path = paths::GLOBAL_REGEX_SET.matches(uri.path());

        // This match statement is duplicated below in `parse_operation_id()`.
        // Please update both places if changing how this code is autogenerated.
        match &method {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
            // {{{operationId}}} - {{{httpMethod}}} {{{path}}}
            &hyper::Method::{{vendorExtensions.x-http-method}} if path.matched(paths::ID_{{vendorExtensions.x-path-id}}) => {
{{#hasAuthMethods}}
                {
                    let authorization = match (&context as &dyn Has<Option<Authorization>>).get() {
                        &Some(ref authorization) => authorization,
                        &None => return Box::new(future::ok(Response::new()
                                                .with_status(StatusCode::Forbidden)
                                                .with_body("Unauthenticated"))),
                    };

                    {{#authMethods}}
                    {{#isOAuth}}
                    // Authorization
                    if let Scopes::Some(ref scopes) = authorization.scopes {
                        let required_scopes: BTreeSet<String> = vec![
                            {{#scopes}}
                            "{{{scope}}}".to_string(), // {{{description}}}
                            {{/scopes}}
                        ].into_iter().collect();

                        if !required_scopes.is_subset(scopes) {
                            let missing_scopes = required_scopes.difference(scopes);
                            return Box::new(future::ok(Response::new()
                                .with_status(StatusCode::Forbidden)
                                .with_body(missing_scopes.fold(
                                    "Insufficient authorization, missing scopes".to_string(),
                                    |s, scope| format!("{} {}", s, scope)
                                ))
                            ));
                        }
                    }
                    {{/isOAuth}}
                    {{/authMethods}}
                }
{{/hasAuthMethods}}
{{#vendorExtensions}}
  {{#consumesMultipart}} 
                let boundary = match multipart_boundary(&headers) {
                    Some(boundary) => boundary,
                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Couldn't find valid multipart body"))),
                };
  {{/consumesMultipart}}
  {{#hasPathParams}}
                // Path parameters
                let path = uri.path().to_string();
                let path_params =
                    paths::REGEX_{{{PATH_ID}}}
                    .captures(&path)
                    .unwrap_or_else(||
                        panic!("Path {} matched RE {{{PATH_ID}}} in set but failed match against \"{}\"", path, paths::REGEX_{{{PATH_ID}}}.as_str())
                    );
  {{/hasPathParams}}
{{/vendorExtensions}}
{{#pathParams}}
                let param_{{{paramName}}} = match percent_encoding::percent_decode(path_params["{{{paramName}}}"].as_bytes()).decode_utf8() {
                    Ok(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
                        Ok(param_{{{paramName}}}) => param_{{{paramName}}},
                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse path parameter {{{baseName}}}: {:?}", e)))),
                    },
                    Err(_) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{paramName}}}"]))))
                };
{{/pathParams}}
{{#headerParams}}
  {{#-first}}
                // Header parameters
  {{/-first}}
                header! { (Request{{vendorExtensions.x-type-name}}, "{{{baseName}}}") => {{#isListContainer}}({{{baseType}}})*{{/isListContainer}}{{^isListContainer}}[{{{dataType}}}]{{/isListContainer}} }
  {{#required}}
                let param_{{{paramName}}} = match headers.get::<Request{{vendorExtensions.x-type-name}}>() {
                    Some(param_{{{paramName}}}) => param_{{{paramName}}}.0.clone(),
                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing or invalid required header {{{baseName}}}"))),
                };
  {{/required}}
  {{^required}}
                let param_{{{paramName}}} = headers.safe_get::<Request{{vendorExtensions.x-type-name}}>().map(|header| header.0.clone());
  {{/required}}
{{/headerParams}}
{{#queryParams}}
  {{#-first}}
                // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response)
                let query_params = form_urlencoded::parse(uri.query().unwrap_or_default().as_bytes()).collect::<Vec<_>>();
  {{/-first}}
                let param_{{{paramName}}} = query_params.iter().filter(|e| e.0 == "{{{baseName}}}").map(|e| e.1.to_owned())
  {{#isListContainer}}
                    .filter_map(|param_{{{paramName}}}| param_{{{paramName}}}.parse::<{{{baseType}}}>().ok())
                    .collect::<Vec<_>>();
  {{^required}}
                let param_{{{paramName}}} = if !param_{{{paramName}}}.is_empty() {
                    Some(param_{{{paramName}}})
                } else {
                    None
                };
  {{/required}}
{{/isListContainer}}
{{^isListContainer}}
                    .nth(0);
{{#required}}
                let param_{{{paramName}}} = match param_{{{paramName}}} {
                    Some(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
                        Ok(param_{{{paramName}}}) => param_{{{paramName}}},
                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse query parameter {{{baseName}}} - doesn't match schema: {}", e)))),
                    },
                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing required query parameter {{{baseName}}}"))),
                };
{{/required}}{{^required}}
                let param_{{{paramName}}} = param_{{{paramName}}}.and_then(|param_{{{paramName}}}| param_{{{paramName}}}.parse::<{{{baseType}}}>().ok());
{{/required}}
{{/isListContainer}}
{{/queryParams}}
{{#vendorExtensions}}
  {{^consumesMultipart}}
    {{#bodyParams}}
      {{#-first}}
                // Body parameters (note that non-required body parameters will ignore garbage
                // values, rather than causing a 400 response). Produce warning header and logs for
                // any unused fields.
                Box::new(body.concat2()
                    .then(move |result| -> Box<dyn Future<Item=Response, Error=Error>> {
                        match result {
                            Ok(body) => {
        {{#vendorExtensions}}
          {{^consumesPlainText}}
                                let mut unused_elements = Vec::new();
          {{/consumesPlainText}}
                                let param_{{{paramName}}}: Option<{{{dataType}}}> = if !body.is_empty() {
          {{#consumesXml}}
                                    let deserializer = &mut serde_xml_rs::de::Deserializer::new_from_reader(&*body);
          {{/consumesXml}}
          {{#consumesJson}}
                                    let deserializer = &mut serde_json::Deserializer::from_slice(&*body);
          {{/consumesJson}}
          {{^consumesPlainText}}
                                    match serde_ignored::deserialize(deserializer, |path| {
                                            warn!("Ignoring unknown field in body: {}", path);
                                            unused_elements.push(path.to_string());
                                    }) {
                                        Ok(param_{{{paramName}}}) => param_{{{paramName}}},
            {{#required}}
                                        Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse body parameter {{{baseName}}} - doesn't match schema: {}", e)))),
            {{/required}}
            {{^required}}
                                        Err(_) => None,
            {{/required}}
                                    }
          {{/consumesPlainText}}
          {{#consumesPlainText}}
            {{#isByteArray}}
                                    Some(swagger::ByteArray(body.to_vec()))
            {{/isByteArray}}
            {{#isString}}
                                    Some(String::from_utf8(body.to_vec()).unwrap())
            {{/isString}}
          {{/consumesPlainText}}
        {{/vendorExtensions}}
                                } else {
                                    None
                                };
        {{#required}}
                                let param_{{{paramName}}} = match param_{{{paramName}}} {
                                    Some(param_{{{paramName}}}) => param_{{{paramName}}},
                                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing required body parameter {{{baseName}}}"))),
                                };
        {{/required}}
      {{/-first}}
    {{/bodyParams}}
  {{/consumesMultipart}}
  {{#consumesMultipart}}
    {{^bodyParams}}
      {{#vendorExtensions}}
                // Form Body parameters (note that non-required body parameters will ignore garbage
                // values, rather than causing a 400 response). Produce warning header and logs for
                // any unused fields.
                Box::new(body.concat2()
                    .then(move |result| -> Box<dyn Future<Item=Response, Error=Error>> {
                        match result {
                            Ok(body) => {
                                // Read Form Parameters from body
                                let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary).save().temp() {
                                    SaveResult::Full(entries) => {
                                        entries
                                    },
                                    _ => {
                                        return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Unable to process all message parts"))))
                                    },
                                };
                {{#formParams}}{{#-first}}{{/-first}}
                                {{#isByteArray}}
                                let file_{{{paramName}}} = entries.files.remove("{{{paramName}}}");
                                {{#required}}
                                let param_{{{paramName}}} = match file_{{{paramName}}} {
                                    Some(file) => {
                                        let path = &file[0].path;
                                        let {{{paramName}}}_str = fs::read_to_string(path).unwrap();
                                        swagger::ByteArray({{{paramName}}}_str.as_bytes().to_vec())
                                    }
                                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Missing required form parameter {{{paramName}}}")))),
                                };
                                {{/required}}
                                {{^required}}
                                let param_{{{paramName}}} = match file_{{{paramName}}} {
                                    Some(file) => {
                                        let path = &file[0].path;
                                        let {{{paramName}}}_str = fs::read_to_string(path).unwrap();
                                        Some(swagger::ByteArray({{{paramName}}}_str.as_bytes().to_vec()))
                                    }
                                    None => None,
                                };
                                {{/required}}
                                {{/isByteArray}}
                                {{^isByteArray}}{{#jsonSchema}}
                                let file_{{{paramName}}} = entries.files.remove("{{{paramName}}}");
                                {{#required}}
                                let param_{{{paramName}}} = match file_{{{paramName}}} {
                                    Some(file) => {
                                        let path = &file[0].path;
                                        let {{{paramName}}}_str = fs::read_to_string(path).expect("Reading saved String should never fail");
                                        let {{{paramName}}}_model: {{{dataType}}} = match serde_json::from_str(&{{{paramName}}}_str) {
                                            Ok(model) => model,
                                            Err(e) => {
                                                return Box::new(future::ok(
                                                    Response::new()
                                                    .with_status(StatusCode::BadRequest)
                                                    .with_body(format!("{{{paramName}}} data does not match API definition: {}", e))))
                                            }
                                        };
                                        {{{paramName}}}_model
                                    }
                                    None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Missing required form parameter {{{paramName}}}")))),
                                };
                                {{/required}}
                                {{^required}}
                                let param_{{{paramName}}} = match file_{{{paramName}}} {
                                   Some(file) => {
                                        let path = &file[0].path;
                                        let {{{paramName}}}_str = fs::read_to_string(path).unwrap();
                                        let {{{paramName}}}_model: {{{dataType}}} = serde_json::from_str(&{{{paramName}}}_str).expect("Impossible to fail to serialise");
                                        Some({{{paramName}}}_model)
                                    }
                                    None => None,
                                };
                                {{/required}}
                                {{/jsonSchema}}{{/isByteArray}}
        {{/formParams}}
      {{/vendorExtensions}}
    {{/bodyParams}}
  {{/consumesMultipart}}
  {{^consumesMultipart}}
    {{^bodyParams}}
      {{#vendorExtensions}}
                Box::new({
                        {{
        {{#formParams}}
          {{#-first}}
                                // Form parameters
          {{/-first}}
                                let param_{{{paramName}}} = {{^isContainer}}{{#vendorExtensions}}{{{example}}};{{/vendorExtensions}}{{/isContainer}}{{#isListContainer}}{{#required}}Vec::new();{{/required}}{{^required}}None;{{/required}}{{/isListContainer}}{{#isMapContainer}}None;{{/isMapContainer}}
        {{/formParams}}
      {{/vendorExtensions}}
    {{/bodyParams}}
  {{/consumesMultipart}}
{{/vendorExtensions}}
                                Box::new(api_impl.{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}param_{{{paramName}}}{{#isListContainer}}.as_ref(){{/isListContainer}}, {{/allParams}}&context)
                                    .then(move |result| {
                                        let mut response = Response::new();
                                        response.headers_mut().set(XSpanId((&context as &dyn Has<XSpanIdString>).get().0.to_string()));
{{#bodyParams}}{{#vendorExtensions}}{{^consumesPlainText}}
                                        if !unused_elements.is_empty() {
                                            response.headers_mut().set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements)));
                                        }
{{/consumesPlainText}}{{/vendorExtensions}}{{/bodyParams}}
                                        match result {
                                            Ok(rsp) => match rsp {
{{#responses}}
                                                {{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}
{{#dataType}}{{^headers}}
                                                    (body)
{{/headers}}{{#headers}}
{{#-first}}
                                                    {
                                                        body,
{{/-first}}
                                                        {{{name}}}{{^-last}}, {{/-last}}
{{#-last}}
                                                    }
{{/-last}}
{{/headers}}{{/dataType}}
{{^dataType}}{{#headers}}{{#-first}}
                                                    {
{{/-first}}
                                                        {{{name}}}{{^-last}}, {{/-last}}
{{#-last}}
                                                    }
{{/-last}}
{{/headers}}{{/dataType}}
                                                => {
                                                    response.set_status(StatusCode::try_from({{{code}}}).unwrap());
{{#headers}}
                                                    header! { (Response{{{nameInCamelCase}}}, "{{{baseName}}}") => [{{{dataType}}}] }
                                                    response.headers_mut().set(Response{{{nameInCamelCase}}}({{{name}}}));
{{/headers}}
{{#produces}}{{#-first}}{{#dataType}}
                                                    response.headers_mut().set(ContentType(mimetypes::responses::{{#vendorExtensions}}{{{uppercase_operation_id}}}_{{x-uppercaseResponseId}}{{/vendorExtensions}}.clone()));
{{/dataType}}{{/-first}}{{/produces}}
{{#dataType}}
{{#vendorExtensions}}
  {{#producesXml}}
    {{^has_namespace}}
                                                    let body = serde_xml_rs::to_string(&body).expect("impossible to fail to serialize");
    {{/has_namespace}}
    {{#has_namespace}}
                                                    let mut namespaces = BTreeMap::new();
                                                    // An empty string is used to indicate a global namespace in xmltree.
                                                    namespaces.insert("".to_string(), {{{dataType}}}::NAMESPACE.to_string());
                                                    let body = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");
    {{/has_namespace}}
  {{/producesXml}}
  {{#producesJson}}
                                                    let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
  {{/producesJson}}
  {{#producesBytes}}
                                                    let body = body.0;
  {{/producesBytes}}
  {{#producesPlainText}}
                                                    let body = body;
  {{/producesPlainText}}
{{/vendorExtensions}}
                                                    response.set_body(body);
{{/dataType}}
                                                },
{{/responses}}
                                            },
                                            Err(_) => {
                                                // Application code returned an error. This should not happen, as the implementation should
                                                // return a valid response.
                                                response.set_status(StatusCode::InternalServerError);
                                                response.set_body("An internal error occurred");
                                            },
                                        }

                                        future::ok(response)
                                    }
                                ))
{{#vendorExtensions}}
  {{^consumesMultipart}}
    {{^bodyParams}}
                        }}
                }) as Box<dyn Future<Item=Response, Error=Error>>
    {{/bodyParams}}
  {{/consumesMultipart}}
{{/vendorExtensions}}
{{#bodyParams}}
  {{#-first}}
                            },
                            Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read body parameter {{{baseName}}}: {}", e)))),
                        }
                    })
                ) as Box<dyn Future<Item=Response, Error=Error>>
  {{/-first}}
{{/bodyParams}}
{{#vendorExtensions}}
  {{#consumesMultipart}}
    {{^bodyParams}}
                                as Box<dyn Future<Item=Response, Error=Error>>
                            },
                            Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read multipart body")))),
                        }
                    })
                )
    {{/bodyParams}}
  {{/consumesMultipart}}
{{/vendorExtensions}}
            },
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
            _ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound))) as Box<dyn Future<Item=Response, Error=Error>>,
        }
    }
}

impl<T, C> Clone for Service<T, C>
{
    fn clone(&self) -> Self {
        Service {
            api_impl: self.api_impl.clone(),
            marker: self.marker.clone(),
        }
    }
}

{{#apiUsesMultipart}}
/// Utility function to get the multipart boundary marker (if any) from the Headers.
fn multipart_boundary(headers: &Headers) -> Option<String> {
    headers.safe_get::<ContentType>().and_then(|content_type| {
        let ContentType(mime) = content_type;
        if mime.type_() == hyper::mime::MULTIPART && mime.subtype() == hyper::mime::FORM_DATA {
            mime.get_param(hyper::mime::BOUNDARY).map(|x| x.as_str().to_string())
        } else {
            None
        }
    })
}
{{/apiUsesMultipart}}

/// Request parser for `Api`.
pub struct ApiRequestParser;
impl RequestParser for ApiRequestParser {
    fn parse_operation_id(request: &Request) -> Result<&'static str, ()> {
        let path = paths::GLOBAL_REGEX_SET.matches(request.uri().path());
        match request.method() {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
            // {{{operationId}}} - {{{httpMethod}}} {{{path}}}
            &hyper::Method::{{{vendorExtensions.x-http-method}}} if path.matched(paths::ID_{{{vendorExtensions.x-path-id}}}) => Ok("{{{operationId}}}"),
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}            _ => Err(()),
        }
    }
}
